const PENDDING = "PENDDING";
const RESOLVE = "FULLFILLLED";
const REJECT = "REJECTED";


function MyPromise(exactor) {
  this.state = PENDDING;
  this.resolveCallBacks = [];
  this.rejectCallBacks = [];
  this.value = void 0;
  this.reason = void 0;

  const that = this;

  function resolve(value) {
    setTimeout(() => {
      if (that.state === PENDDING) {
        that.state = RESOLVE;
        that.value = value;
        that.resolveCallBacks.forEach(fn => {
          fn(that.value);
        });
      }
    }, 0);
  }

  function reject(reason) {
    setTimeout(() => {
      if (that.state === PENDDING) {
        that.state = REJECT;
        that.reason = reason;
        that.rejectCallBacks.forEach(fn => {
          fn(that.reason);
        });
      }
    }, 0);
  }
  // 执行
  try {
    exactor(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

function resolvePromise(p, result, resolve, reject) {
  if (p === result) {
    return reject(new Error('循环引用'));
  }
  let called = false;
  // 判断result的值是否是promise或普通类型
  if (result instanceof p.constructor) { // result 是promise
    if (result.state === PENDDING) {
      result.then(y => {
        resolvePromise(p, y, resolve, reject);
      }, reason => reject(reason));
    } else {
      result.then(resolve, reject);
    }
  } else if (result != null &&
    (typeof result === "object" || typeof result === "function")) {
    try {
      let then = result.then;
      if (typeof then === 'function') {
        then.call(result, y => {
          if (called) return;
          called = true;
          resolve(y);
        }, r => {
          if (called) return;
          called = true;
          reject(r);
        });
      } else { // 没有then 方法
        resolve(result);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(result);
  }

}

/* 
  Promise.then
*/
MyPromise.prototype.then = function (onFullfill, onReject) {
  onFullfill = typeof onFullfill === 'function' ? onFullfill : value => value;
  onReject = typeof onReject === 'function' ? onReject : reason => reason;

  const that = this;
  let p;
  if (that.state === RESOLVE) {
    p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        let result = onFullfill(that.value);
        resolvePromise(p, result, resolve, reject);
      }, 0);
    });

  } else if (that.state === REJECT) {
    p = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        let result = onReject(that.value);
        resolvePromise(p, result, resolve, reject);
      }, 0);
    });

  } else if (that.state === PENDDING) {
    p = new MyPromise((resolve, reject) => {
      that.resolveCallBacks.push(value => {
        try {
          let res = onFullfill(value);
          resolvePromise(p, res, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
      that.rejectCallBacks.push(value => {
        try {
          let res = onReject(value);
          resolvePromise(p, res, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    });

  }

  return p;
}

/* 
  Promise.catch
*/
MyPromise.prototype.catch = function (onReject) {
  return this.then(null, onReject);
}

/* Promise.finally */

MyPromise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(val => {
    P.resolve(callback()).then(() => val);
  }, reason => {
    P.resolve(callback()).then(() => {throw reason});
  });
}

/* 
  resolve,reject
*/
MyPromise.resolve = function (val) {
  return new MyPromise(resolve => resolve(val));
}
MyPromise.reject = function (reason) {
  return new MyPromise((resolve, reject) => reject(reason));
}

/*  
  all,race
*/
MyPromise.all = function (pArr) {
  return new Promise((resolve, reject) => {
    var resolveVal = resolveAll(pArr.length, resolve);
    pArr.forEach((p, i) => {
      p.then(v => resolveVal(i, v), e => reject(e));
    });
  });
}

function resolveAll(length, resolve) {
  let count = 0;
  let vals = [];
  return function (i, val) {
    vals[i] = val;
    if (++count === length) {
      resolve(vals);
    }
  };
}

MyPromise.race = function (pArr) {
  return new MyPromise((resolve, reject) => {
    let res =
      pArr.forEach((p, i) => {
        p.then(v => {

        }, r => {
          reject(r)
        });
      });
  });
}





const p = new MyPromise(function (resolve, rej) {

  setTimeout(() => {
    resolve(1111);
    //rej('aaa');
  }, 1000);
})

p.then((v) => {
    console.log(v);
    return 0;
  }, reason => {
    console.log(reason);
  })
  .then(v => console.log(v));
